<?php
/**
 * DataStore_Mysql
 * 
 * 作者:  姚刚 (yaogang@the9sense.com)
 * 创建时间: 2011-03-06 03:27:18
 * 修改记录: 
 * 
 * $Id: Mysql.php 18 2011-04-17 12:50:07Z coosuu $
 */

class DataStore_Mysql {

    /**
     * __construct 
     * 
     * @param  mixed $table 
     * @param  mixed $config 
     * @return void
     */
    public function __construct($table, $config = array()) {

        $this->_table = $table;
        $this->_config = !$config ? Config::get('DB.' . $table) : $config;

        if (!$this->_config || !is_array($this->_config)) {
            throw new DataStore_Exception($table . ' config cannot be empty');
        }
    }

    /**
     * getRow 
     * 
     * @param  mixed $conditions 
     * @param  array $params 
     * @param  string $columns 
     * @return void
     */
    public function getRow($conditions = null, $params = array(), $columns = array('*')) {

        $dbTables = $this->_shard(null, $conditions, $params);
        $result = array();

        try {
            foreach ($dbTables as $t) {
                $result = $t->getRow($conditions, $params, $columns);
                if ($result && is_array($result)) {
                    return $result;
                }
            }
        } catch (PDOException $e) {
            throw new DataStore_Exception($e);
        }

        return $result;
    }

    /**
     * getOne 
     * 
     * @param  mixed $conditions 
     * @param  array $params 
     * @param  string $columns 
     * @return void
     */
    public function getOne($conditions = null, $params = array(), $columns = array('*')) {

        $dbTables = $this->_shard(null, $conditions, $params);
        $result = null;

        try {
            foreach ($dbTables as $t) {
                $result = $t->getOne($conditions, $params, $columns);
                if ($result) {
                    return $result;
                }
            }
        } catch (PDOException $e) {
            throw new DataStore_Exception($e);
        }

        return $result;
    }

    /**
     * getAll 
     * 
     * @param  mixed $conditions 
     * @param  array $params 
     * @param  string $columns 
     * @param  array $pageOptions 
     * @param  array $order 
     * @return void
     */
    public function getAll($conditions = null, $params = array(), $columns = array('*'), $start = 0, $limit = 0, $order = array()) {

        $dbTables = $this->_shard(null, $conditions, $params);
        $result = array();

        try {
            foreach ($dbTables as $t) {
                $tmp = $t->getAll($conditions, $params, $columns, $start, $limit, $order);
                if ($tmp && is_array($tmp)) {
                    $result = array_merge($result, $tmp);
                }
            }
        } catch (PDOException $e) {
            throw new DataStore_Exception($e);
        }

        return $result;
    }

    /**
     * save 
     * 
     * @param  mixed $data 
     * @param  mixed $conditions 
     * @param  array $params 
     * @return void
     */
    public function save($data, $conditions = null, $params = array()) {

        $dbTables = $this->_shard($data, $conditions, $params);
        try {
            foreach ($dbTables as $t) {
                $t->save($data, $conditions, $params);
                if ($t->getLastInsertId()) {
                    $this->_lastInsertId = $t->getLastInsertId();
                }
            }
        } catch (PDOException $e) {
            throw new DataStore_Exception($e);
        }
        return true;
    }

    /**
     * count 
     * 统计
     * 
     * @param  mixed $conditions 
     * @param  array $params 
     * @return void
     */
    public function count($conditions = null, $params = array()) {

        $count =  0;
        $dbTables = $this->_shard($conditions, $params);
        try {
            foreach ($dbTables as $t) {
                $count += $t->count($conditions, $params);
            }
        } catch (PDOException $e) {
            throw new DataStore_Exception($e);
        }
        return $count;
    }

    /**
     * getLastInsertId 
     * 
     * @return void
     */
    public function getLastInsertId() {

        return $this->_lastInsertId;
    }

    public function incr() {

    }

    public function decr() {

    }

    /**
     * remove 
     * 
     * @param  mixed $conditions 
     * @param  array $params 
     * @return void
     */
    public function remove($conditions = null, $params = array()) {

        $dbTables = $this->_shard(null, $conditions, $params);
        try {
            foreach ($dbTables as $t) {
                $t->remove($conditions, $params);
            }
        } catch (PDOException $e) {
            throw new DataStore_Exception($e);
        }

        return true;
    }

    /**
     * truncate 
     * 
     * @return void
     */
    public function truncate() {

        $dbTables = $this->_shard();
        try {
            foreach ($dbTables as $t) {
                $t->truncate();
            }
        } catch (PDOException $e) {
            throw new DataStore_Exception($e);
        }
    }

    /**
     * _shard 
     * 
     * @param  array $data 
     * @param  mixed $conditions 
     * @param  array $params 
     * @return void
     */
    private function _shard($data = array(), $conditions = null, $params = array()) {

        $dsn = $this->_config['dsn'];
        $dbuser = $this->_config['dbuser'];
        $password = $this->_config['password'];

        // 如果没分表，直接返回连接
        if (!$this->_isShard()) {
            return array(new DB_Table($this->_table, $dsn, $dbuser, $password));
        }

        // 校验shardKey
        if (!$this->_checkShardKeys()) {
            throw new DataStore_Exception('Shard Config Error');
        }

        // 如果主键是自增，不支持分表Key与主键相同
        if ($this->_isAutoincream() && $this->_shardKeySameAsPk()) {
            throw new DataStore_Exception('not support shardkey as same as pk field:' . $this->_config['fields']['pk']);
        }

        if ($data) {

            if (!is_array($data)) {
                throw new DataStore_Exception('$data should be an array');
            }

            $dataKeys = array_keys($data);
            $dataKeyDiff = array_diff($this->_config['shard']['key'], $dataKeys);
        }

        // 插入
        if ($data && !$conditions) {

            // 检查$data中是不是有shardKey
            if ($dataKeyDiff) {
                throw new DataStore_Exception('Shard key: ' . implode(',', $dataKeyDiff) . ' not found in $data');
            }

            $table = Helper::factory('Table')->getTableName($table, $this->_config['shard'], $data);

            // 尝试寻找该表的配置，如果没有，使用默认配置
            foreach (array('dsn', 'dbuser', 'password') as $check) {
                if ($this->_config['shard'][$table][$check]) {
                    $$check = $this->_config['shard'][$table][$check];
                }
            }

            return array(new DB_Table($table, $dsn, $dbuser, $password));
        }

        // 更新
        if ($data && $conditions) {

            if (!$dataKeyDiff) {
                throw new DataStore_Exception('not support update shardKey');
            }
        }

    }

    /**
     * _isAutoincream 
     * 
     * @return void
     */
    private function _isAutoincream() {

        return !array_key_exists('autoincream', $this->_config) || $this->_config['autoincream'] == true;
    }

    /**
     * _isShard 
     * 
     * @return void
     */
    private function _isShard() {

        if (!array_key_exists('shard', $this->_config) || !$this->_config['shard'] || $this->_config['shard']['num'] == 1) {
            return false;
        }
        return $this->_config['shard'];
    }

    /**
     * _shardKeySameAsPk 
     * 
     * @return void
     */
    private function _shardKeySameAsPk() {

        return implode('_', $this->_config['shard']['key']) == $this->_config['fields']['pk'];
    }

    /**
     * _checkShardKeys 
     * 
     * @return void
     */
    private function _checkShardKeys() {

        if (!$this->_config['shard']['key']) {
            throw new DataStore_Exception('Must set $this->_config["shard"]["key"]');
        }

        // 转化成数组
        if (!is_array($this->_config['shard']['key'])) {
            $this->_config['shard']['key'] = array($this->_config['shard']['key']);
        }

        $diff = array_diff($this->_config['shard']['key'], $this->_config['fields']);
        if ($diff) {
            throw new DataStore_Exception('ShardKey Config Error:Invaild Fields(' . implode(',', $diff) . ')');
        }

        return true;
    }

    private function _parseConditions($conditions, $params) {
    }

}
